--------------------------------------------------------------------------------
-- Team:        Synapse
-- Module:    CPU Top Level
--------------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;


package cpuPackage is
component CPU
  port(
        vsync_n    : in  std_logic;
        clk        : in  std_logic;         -- master clock input
        reset      : in  std_logic;         -- reset

        -- Keyboard stuff
        ps2_clk    : in  std_logic;           -- keyboard clock
        ps2_data   : in  std_logic;           -- keyboard data

        
        -- SDRAM stuff
        rd         : out std_logic;         -- memory read control signal
        wr         : out std_logic;         -- memory write control signal
        addrOut    : out std_logic_vector(17 downto 0);  -- address to memory
        dIn        : in  std_logic_vector(15 downto 0);  -- data from memory
        dOut       : out std_logic_vector(15 downto 0);
                
		  -- Regfile stuff
        RE         : out std_logic;
        WE         : out std_logic;   
        D          : out std_logic_vector(15 downto 0);
        Q          : in std_logic_vector(15 downto 0);  
        A          : out std_logic_vector(3 downto 0)
    );
end component;
end package cpuPackage;

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;


entity CPU is
    Port (                
        vsync_n    : in  std_logic;
        clk        : in  std_logic;    -- master clock input
        reset      : in  std_logic;    -- reset
              
        -- Keyboard stuff
        ps2_clk    : in  std_logic;    -- keyboard clock
        ps2_data   : in  std_logic;    -- keyboard data

        -- SDRAM stuff     
        rd         : out std_logic;    -- memory read control signal
        wr         : out std_logic;    -- memory write control signal
        addrOut    : out std_logic_vector(17 downto 0);  -- address to memory
        dIn        : in  std_logic_vector(15 downto 0);  -- data from memory
        dOut       : out std_logic_vector(15 downto 0);  -- data to memory

        RE         : out std_logic;
        WE         : out std_logic;   
        D          : out std_logic_vector(15 downto 0);
        Q          : in std_logic_vector(15 downto 0);  
        A          : out std_logic_vector(3 downto 0)
    );
end CPU;

architecture Behavioral of CPU is

   component control
    Port (
        clk                    : in    std_logic;
        reset                    : in    std_logic;
        opcode                    : in    std_logic_vector(7 downto 0);
        conditionCode                : in    std_logic_vector(3 downto 0);
        immediate                : in    std_logic_vector(7 downto 0);

                
        flagF                    : in    std_logic;
        flagC                    : in    std_logic;
        flagL                    : in    std_logic;
        flagN                    : in    std_logic;
        flagZ                    : in    std_logic;

        RegWrite_EN                : out std_logic;
        RegRead_EN                         : out std_logic;
        RegisterBOut                       : out std_logic_vector(15 downto 0);
        RegisterAOut                    : out std_logic_vector(15 downto 0);
        RegAddrMux                        : out std_logic;
        Q                               : in std_logic_vector(15 downto 0);  
                                
        memoryRead                : out std_logic;
        memoryWrite                : out std_logic;
        instructionWrite            : out std_logic;
        aluControl                : out std_logic_vector(2 downto 0);
        aluBInputMux                : out std_logic_vector(1 downto 0);
               regAInputMux                : out std_logic_vector(1 downto 0);               
        memoryAddressMux            : out std_logic;
        addToPC                    : out std_logic_vector(7 downto 0);
               nextPCSelectMux                : out std_logic;
            memPage                    : out std_logic_vector(1 downto 0)
    );
    end component;

    component if_id_pc
    Port (    clk                    : in std_logic;
        reset                    : in std_logic;
        instructionWrite            : in std_logic;
        instruction                : in std_logic_vector(15 downto 0);
        nextProgramCounter            : in std_logic_vector(15 downto 0);          

        currentProgramCounter            : out std_logic_vector(15 downto 0);
        opcode                     : out std_logic_vector(7 downto 0);
        addressA                 : out std_logic_vector(3 downto 0);
        addressB                : out std_logic_vector(3 downto 0);
        immediate                : out std_logic_vector(7 downto 0)
    );
    end component;
    
    component ps2
    port(
        clk                          : in  std_logic;           -- main clock
        ps2_clk                  : in  std_logic;           -- keyboard clock
            ps2_data                 : in  std_logic;           -- keyboard data
        keyboardOut                : out std_logic_vector(9 downto 0)
    );
    end component;
                

    component memory_sdram
--    component memory_blockram
    port ( 
        address                    : in    std_logic_vector (17 downto 0); 
        dataOut                    : out    std_logic_vector (15 downto 0); 
        read                    : in    std_logic; 
                 
        vsync_n                    : in    std_logic;
        keyboard                : in    std_logic_vector(9 downto 0);
                
                 -- SDRAM Stuff
            
        rd                        : out std_logic;         -- memory read control signal
        dIn                        : in  std_logic_vector(15 downto 0)  -- data from memory
    );
    end component;

    component alu
    port (
        A                          : in    std_logic_vector (15 downto 0); 
        B                          : in    std_logic_vector (15 downto 0); 
        opcode                     : in    std_logic_vector (2 downto 0); 
        clk                    : in    std_logic;
        reset                      : in    std_logic;
        S                          : out   std_logic_vector (15 downto 0);
                 
        flagF                    : out std_logic := '0';
        flagL                    : out std_logic := '0';
        flagC                    : out std_logic := '0';
        flagN                    : out std_logic := '0';
        flagZ                    : out std_logic := '0'
    );
       end component;



    -- The current and next program counters for the instruction fetch.
    signal currentProgramCounter : std_logic_vector(15 downto 0);
    signal nextProgramCounter : std_logic_vector(15 downto 0);
    
    -- Control Outputs

    signal memoryRead : std_logic;
    signal memoryWrite : std_logic;
    signal instructionWrite : std_logic;
    signal aluControl : std_logic_vector(2 downto 0);
    signal aluBInputMux : std_logic_vector(1 downto 0);
    signal regAInputMux : std_logic_vector(1 downto 0);
    signal memoryAddressMux : std_logic;
    signal addToPC : std_logic_vector(7 downto 0); -- value to add to PC
    signal nextPCSelectMux : std_logic;

    -- Register Outputs/Inputs
        signal RegAddrMux : std_logic;
        signal registerAOut : std_logic_vector( 15 downto 0);
        signal registerBOut : std_logic_vector(15 downto 0);
   
        signal registerAIn : std_logic_vector( 15 downto 0); 
        signal RegRead_EN: std_logic;
        signal RegWrite_EN: std_logic;
    -- ALU Output/Inputs
    -- input for port A is registerAOut
    signal aluBInput : std_logic_vector(15 downto 0);
    signal aluOutput : std_logic_vector(15 downto 0);
    signal flagF      : std_logic;
    signal flagC      : std_logic;
    signal flagL      : std_logic;
    signal flagN      : std_logic;
    signal flagZ      : std_logic;

    -- Outputs from the instruction decode: opcode + extension, the two registers,
    -- and the sign extended immediate 16 bit value. Different instructions will
    -- make use of different outputs of the instruction decode unit.
    signal opcode : std_logic_vector(7 downto 0);
    signal instructionAddressA : std_logic_vector(3 downto 0);
    signal instructionAddressB : std_logic_vector(3 downto 0);
    signal immediate : std_logic_vector(7 downto 0);

    -- Memory Inputs/Outputs
    signal memoryDataOut : std_logic_vector(15 downto 0);
    signal memoryAddress : std_logic_vector(17 downto 0);
    signal memPage : std_logic_vector( 15 downto 0);
    -- Keyboard Output
    signal keyboard    : std_logic_vector(9 downto 0);

    -- Audio Control
    
        signal immed: std_logic_vector ( 15 downto 0);
        signal addToPC15 : std_logic_vector( 15 downto 0);
begin
       memPage(15 downto 2) <= "00000000000000";
       RE <= RegRead_EN;
       WE <= RegWrite_EN;
       addrOut <=memoryAddress;
       wr <=memoryWrite;
       dOut <=registerAOut;
        process(addToPC)
        begin
         if addToPC(7) = '1' then
           addToPC15 <= "11111111" & addToPC;
         else
           addToPC15 <= "00000000" & addToPC;
         end if;
       end process;
       process(immediate)
        begin
         if immediate(7) = '1' then
           immed <= "11111111" & immediate;
         else
           immed <= "00000000" & immediate;
         end if;
         
        end process;
    -- Set the upper memPage bits to 0's.
     
      process (RegAddrMux)
       begin  -- process
             if (RegAddrMux = '1') then
                A <= instructionAddressB;
             else
                A <= instructionAddressA;
             end if;
       end process;      
     process (RegWrite_EN)
       begin  -- process
         if (RegWrite_EN='1')  then
              if (RegAddrMux = '1') then
                D <= memPage;
              else
                D <= registerAIn;
              end if;
         else
            D <= (others => '0'); 
       end if;
     end process;  
    -- This mux is for the input of the B ALU, it is a selection of the registerBOutput,
    -- the zero extended immediate, or the sign extended immediate. If aluBInputMux is "00"
    -- or "01", the selection is registerBOut. If aluBInputMux is "10", the selection is
    -- sign extended, if aluBInputMux is "11", the selection is zero extended.
    aluInputMuxProcess : process (aluBInputMux, immed, registerBOut, reset)
    begin

        if (reset = '0') then
            aluBInput <= "0000000000000000";
        elsif (aluBInputMux = "10") then
            aluBInput <= immed;
        elsif (aluBInputMux = "11") then
            aluBInput <= "00000000" & immed(7 downto 0);
        else
            aluBInput <= registerBOut;
        end if;        

    end process;

    -- This mux is for the "data in" input of register A.
    -- If regAInputMux(1 downto 0):
    --     00: memoryDataOut
    --     01: aluOutput
    --     10: immediate shifted to the upper 8 bits, with zeros in the lower 8 bits
    --     11: The current PC value + 1 that is used in jal when storing PC

        regAInputMuxProcess : process (regAInputMux, memoryDataOut, immed, aluOutput, reset)
    begin

        if (reset = '0') then
                registerAIn <= "0000000000000000";
        elsif (regAInputMux = "00") then
            registerAIn <= memoryDataOut;
        elsif (regAInputMux = "01") then
                registerAIn <= aluOutput;
        elsif (regAInputMux = "10") then
            registerAIn <= immed(7 downto 0) & "00000000";
        else
            registerAIn <= currentProgramCounter + '1';
        end if;

    end process;

    -- This mux is for the address input of the memory.
    -- If memoryAddressMux:
    --     0:    memoryAddress = registerBOut
    --        1:    memoryAddress = currentProgramCounter
    memoryAddressMuxProcess : process (memoryAddressMux, registerBOut, nextProgramCounter, reset,memPage)
    begin

        if (reset = '0') then
            memoryAddress <= "000000000000000000";
        elsif (memoryAddressMux = '0') then            
            memoryAddress <= memPage(1 downto 0) & registerBOut(15 downto 0);
        else
            memoryAddress <= "00" & nextProgramCounter(15 downto 0);--currentProgramCounter;
        end if;        

    end process;

    -- This mux selects the next program counter. The two inputs are from an adder and 
    -- the data output of register B. The adder gets the currentProgramCounter and a
    -- value from control which tells it how much to add to the program counter to get
    -- the next program counter. This value will usually be '1', but sometimes it will
    -- be another value if there is a branch. The value in register B is incase there
    -- is a jump, therefore, the next program counter value will be the address to jump
    -- to.
    -- If nextPCSelectMux:
    --     0: registerBOut
    --        1: adder out
    nextPCSelectMuxProcess : process (nextPCSelectMux, currentProgramCounter, registerBOut, addToPC15, reset)
    begin

        if (reset = '0') then
            nextProgramCounter <= "0000000000000000";
        elsif (nextPCSelectMux = '0') then
            nextProgramCounter <= registerBOut;
        else
            nextProgramCounter <= currentProgramCounter + addToPC15;
        end if;

    end process;


   controlModule : control
      port map (clk => clk,
                     reset => reset,
                                         opcode => opcode,
                     conditionCode => instructionAddressA,
                     immediate => immediate,

                     flagF => flagF,
                     flagC => flagC,
                     flagL => flagL,
                     flagN => flagN,
                     flagZ => flagZ,

                     RegWrite_EN => RegWrite_EN,
                                         RegRead_EN => RegRead_EN,
                                         RegAddrMux => RegAddrMux,
                                         registerBOut => registerBOut,
                                         registerAOut => registerAOut,
                                         Q => Q,
                     memoryRead => memoryRead,
                     memoryWrite => memoryWrite,
                     instructionWrite => instructionWrite,
                     aluControl => aluControl,
                     aluBInputMux => aluBInputMux,
                                    regAInputMux => regAInputMux,
                     memoryAddressMux => memoryAddressMux,
                     addToPC => addToPC,
                     nextPCSelectMux => nextPCSelectMux,
                memPage => memPage(1 downto 0)
);
          
    instructionFetchDecodeModule : if_id_pc
        port map    (clk => clk,
                     reset => reset,
                     instructionWrite => instructionWrite,
                     instruction => memoryDataOut,
                     nextProgramCounter => nextProgramCounter,
                     currentProgramCounter => currentProgramCounter,
                     opcode => opcode,
                     addressA => instructionAddressA,
                     addressB => instructionAddressB,
                     immediate => immediate);
                     
    keyboardModule : ps2
        port map    (
                    ps2_clk => ps2_clk,
                                        ps2_data => ps2_data,
                    clk => clk, -- main clock
                    keyboardOut => keyboard);
            
    memoryModule : memory_sdram
      port map (             address => memoryAddress,
                                         dataOut => memoryDataOut,
                                         read => memoryRead,
                        vsync_n => vsync_n,
                     keyboard => keyboard,
                    
                     
                     -- SDRAM Interface Stuff
                     
                      rd => rd,
                      dIn => dIn
                          );

   aluModule : alu
      port map (A => registerAOut,
                B => aluBInput,
                S => aluOutput,
                     flagF => flagF,
                     flagC => flagC,
                     flagL => flagL,
                     flagN => flagN,
                     flagZ => flagZ,
                opcode => aluControl,
                     clk => clk,
                reset => reset);


  
end Behavioral;
